#ifndef _TCPSOCKET_H_
#define _TCPSOCKET_H_

#include <string>

#include "inet.h"

/**
 * Esta clase encapsula la API de sockets.
 */
class TCPSocket {
protected:
	/**
	 * File descriptor del socket. Si el socket a�n no fue abierto,
	 * el valor de esta variable es -1.
	 */
	int sockfd;

public:
	/**
	 * Construye un socket desconectado.
	 */
	TCPSocket();

	/**
	 * Construye un socket en base a un file descriptor ya existente en el
	 * sistema.
	 */
	TCPSocket(int sockfd);

	/**
	 * Constructor de copia de sockets.
	 */
	TCPSocket(const TCPSocket& sock);

	/**
	 * Operador =
	 */
	TCPSocket& operator=(const TCPSocket& sock);

	/**
	 * Devuelve el file descriptor del socket.
	 */
	int get_fd();

	/**
	 * Fuerza la escritura de size bytes en el socket.
	 */
	template <typename T>
	bool send( const T* data) {
		int enviados;	/* Cantidad de bytes enviados. */
		int ult_envio;	/* Cantidad de bytes enviados en el
								�ltimo intento realizado. */
		int size = sizeof(T); /* Tama�o del objeto a enviar. */

		enviados = 0;
		while(size) {
			ult_envio = write(sockfd, ((unsigned char *) data) + enviados,
					size);
			if (ult_envio <= 0) { /* error */
				return false;
			}
			enviados += ult_envio;
			size -= ult_envio;
		}

		return true;
	}

	/**
	 * Fuerza la lectura de size bytes desde el socket.
	 */
	template <typename T>
	bool recv(T* data) {
		int ult_lec;	/* Cantidad de bytes leidos en la
								ultima lectura. */
		int leidos;	/* Cantidad de bytes leidos. */
		int size = sizeof(T); /* Tama�o del objeto a recibir. */

		leidos = 0;
		while(size) {
			ult_lec = read(sockfd, ((unsigned char *) data) + leidos,
					size);
			if (ult_lec <= 0) { /* error */
				return false;
			}
			leidos += ult_lec;
			size -= ult_lec;
		}

		return true;
	}

	/**
	 * Cierra el socket.
	 */
	void close();

    /**
	 * Realiza un open activo con el equipo de nombre host en el puerto port.
	 * No debe invocarse si se invoc� a listen() anteriormente.
	 */
	bool connect(const std::string& host, int port);

	/**
	 * Configura el socket como pasivo en el puerto port, con una cola de espera
	 * de backlog clientes. No debe invocarse si se invoc� a connect() anteriormente.
	 */
	bool listen(int port, int backlog);

	/**
	 * Devuelve true si el socket tiene un file descriptor v�lido.
	 */
    operator bool();

	/**
	 * Acepta una conexi�n en el puerto pasado a listen(). Previamente
	 * debe haberse invocado el m�todo listen().
	 */
	TCPSocket accept();
};

#endif
